/*
 * Project it.battlehorse.rcp.tools.dbbase
 * Created on May 14, 2006
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * A copy of the LGPL is available also at
 * http://www.gnu.org/copyleft/lesser.html
 * 
 */ 
package it.battlehorse.rcp.tools.dbbase.internal;

import it.battlehorse.rcp.sl.annot.Injected;
import it.battlehorse.rcp.sl.annot.Serviceable;
import it.battlehorse.rcp.tools.dbbase.DatasourceConfig;
import it.battlehorse.rcp.tools.dbbase.DbConfig;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import org.apache.commons.logging.Log;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.preference.IPreferenceStore;

/**
 * Provides helper methods when dealing with the store and load of datasources and database configurations.
 * 
 * @author battlehorse
 * @since May 14, 2006
 */
@Serviceable
public class DbBaseStoreHelper {
	
	/* A reference to the db manager */
	private DbManager manager;
	
	/* A reference to a valid logger */
	private Log log;
	
	@Injected
	public void setLog(Log log) {
		this.log = log;
	}
	
	/**
	 * Creates a new instance of the class
	 * 
	 * @param manager the referring database manager
	 */
	public DbBaseStoreHelper(DbManager manager) {
		this.manager = manager;
	}
	
	/**
	 * Loads the configured datasources from the preferences store
	 * 
	 * @param store from the preferences store
	 * @return the configured datasources
	 */
	public List<DatasourceConfig> loadDatasourceConfig(IPreferenceStore store) {
		List<DatasourceConfig> l = new ArrayList<DatasourceConfig>();
		
		String sources = store.getString("DbBasePreferences.configuredSources");
		if (sources != null) {
			StringTokenizer st = new StringTokenizer(sources,",");
			while (st.hasMoreTokens()) {
				String source = st.nextToken().trim();
				l.add(loadDatasource(store, source));
			}
		}
		
		return l;

	}
	
	/**
	 * Stores the given datasources configuration into the preferences store
	 * 
	 * @param store the preferences store
	 * @param l the list of the configured datasources
	 */
	public void storeDatasourceConfig(IPreferenceStore store,List<DatasourceConfig> l) {
		//
		// Clear the existing configuration
		//
		String sources = store.getString("DbBasePreferences.configuredSources");
		if (sources != null) {
			StringTokenizer st = new StringTokenizer(sources,",");
			while (st.hasMoreTokens()) {				
				clearDatasource(store,st.nextToken().trim()); 
			}
		}		
		store.setToDefault("DbBasePreferences.configuredSources");
		
		//
		// Stores the new values
		//
		StringBuilder sb = new StringBuilder();
		for (DatasourceConfig cfg : l) {
			sb.append(cfg.getName()).append(",");
			storeDatasource(store, cfg);
		}
		store.setValue("DbBasePreferences.configuredSources", sb.toString());
	}
	
	/**
	 * Load the configuration data for the specific datasource identified by the given name.
	 * 
	 * @param store the preferences store
	 * @param dsName the datasource name
	 * @return the configured object
	 */
	private DatasourceConfig loadDatasource(IPreferenceStore store,String dsName) {		
		String name = store.getString("DbBasePreferences.configuredSources." + dsName + ".name");
		String dbase = store.getString("DbBasePreferences.configuredSources." + dsName + ".dbase");
		String url = store.getString("DbBasePreferences.configuredSources." + dsName + ".url");
		String user = store.getString("DbBasePreferences.configuredSources." + dsName + ".user");
		String pwd = store.getString("DbBasePreferences.configuredSources." + dsName + ".password");
		
		DbConfig dbcfg = manager.findDbConfig(dbase);
		if (dbcfg != null) {
			return new DatasourceConfig(dbcfg,url,user,pwd,name);
		}
		else {
			// TODO : handle misconfigured DataSources here
			return null;
		}
		
		
	}
	
	/**
	 * Stores into the preferences store the given datasource object
	 * 
	 * @param store the preferences store 
	 * @param ds the Datasource object to be stored
	 */
	private void storeDatasource(IPreferenceStore store, DatasourceConfig ds) {
		String dsName = ds.getName();
		
		store.setValue("DbBasePreferences.configuredSources." + dsName + ".name", ds.getName());
		store.setValue("DbBasePreferences.configuredSources." + dsName + ".dbase", ds.getDatabase().getName());
		store.setValue("DbBasePreferences.configuredSources." + dsName + ".url", ds.getConnectionUrl());
		store.setValue("DbBasePreferences.configuredSources." + dsName + ".user", ds.getUser());
		store.setValue("DbBasePreferences.configuredSources." + dsName + ".password", ds.getPassword());
		
	}
	
	/**
	 * Clears a datasource configuration from the preferences store
	 * 
	 * @param store the preferences store
	 * @param dsName the datasource configuration to be cleared
	 */
	private void clearDatasource(IPreferenceStore store, String dsName) {
		store.setToDefault("DbBasePreferences.configuredSources." + dsName + ".name");
		store.setToDefault("DbBasePreferences.configuredSources." + dsName + ".dbase");
		store.setToDefault("DbBasePreferences.configuredSources." + dsName + ".url");
		store.setToDefault("DbBasePreferences.configuredSources." + dsName + ".user");
		store.setToDefault("DbBasePreferences.configuredSources." + dsName + ".password");
		
	}	
	
	/**
	 * Loads all the available databases ({@code DbConfig} instances) from
	 * the available extensions to the extension point {@code it.battlehorse.rcp.tools.dbbase.database }.
	 * 
	 * @return a list of the available database, or {@code null} if an error 
	 * 		occurred (or no databases have been found).
	 */
	public List<DbConfig> loadDbConfig() {
		
		List<DbConfig> configs = new ArrayList<DbConfig>();
		
		IExtension[] extensions = Platform.getExtensionRegistry().getExtensionPoint("it.battlehorse.rcp.tools.dbbase.database").getExtensions();
    	if (extensions != null)
    		for (int i = 0; i < extensions.length; i++) {
    			String ns = extensions[i].getNamespace();

    			IConfigurationElement[] confs = extensions[i].getConfigurationElements();
    			for (int j = 0; j < confs.length ; j++) {
    				IConfigurationElement conf = confs[j];
    				if (conf.getName().equals("database")) {
    					configs.add(new DbConfig(conf,ns));    					    				    				
    				}	
    			}
    		}	
    	
    	// Verifying drivers
    	
    	Iterator<DbConfig> it = configs.iterator();
    	while (it.hasNext()) {
    		DbConfig cfg = it.next();
    		if (!existsDriver(cfg.getDriverClassName())) {
    			log.trace("Driver " + cfg.getDriverClassName() + " not found. Maybe the fragment has not been correctly configured?");
    			it.remove();
    		}
    	}
    	
    	return configs;

	}
	
	/**
	 * Identifies if a given database JDBC driver exists or not. Identification is
	 * done asking the classLoader for the driver class.
	 *  
	 * @param driverClass the driver class
	 * @return {@code true} if the driver class is found, {@code false} otherwise
	 */
	private boolean existsDriver(String driverClass) {
		boolean found = true;
		try {
			Class.forName(driverClass);
		}
		catch(ClassNotFoundException e) {
			found = false;
		}		
		return found;
	}	

}
